/**
* This work is licensed under the Creative Commons Attribution-NonCommercial-
* NoDerivs 3.0 Unported License. To view a copy of this license, visit
* http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to
* Creative Commons, 444 Castro Street, Suite 900, Mountain View, California,
* 94041, USA.
*
* Use of this work is permitted only in accordance with license rights granted.
* Materials provided "AS IS"; no representations or warranties provided.
*
* Copyright � 2012 Marcus Parkkinen, Aki K�kel�, Fredrik �hs.
**/
package edu.chalmers.dat255.audiobookplayer.view;
import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import edu.chalmers.dat255.audiobookplayer.R;
import edu.chalmers.dat255.audiobookplayer.constants.Constants;
import edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfGUIEvents;
import edu.chalmers.dat255.audiobookplayer.model.Bookshelf;
import edu.chalmers.dat255.audiobookplayer.util.TextFormatter;
/**
* Graphical representation of the bookshelf.
*
* @author Marcus Parkkinen, Fredrik �hs
* @version 0.6
*/
public class BookshelfFragment extends Fragment {
private static final String TAG = "BookshelfFragment.class";
private ExpandableBookshelfAdapter adapter;
private IBookshelfGUIEvents fragmentOwner;
private ExpandableListView bookshelfList;
/**
* Simple interface forcing the enum classes to provide a method to get
* text.
*
*/
private interface IContextMenuItem {
/**
* This method should be used to provide the text to be shown by the
* menu items.
*
* @return The text to be displayed.
*/
String getText();
}
/**
* Enum providing name and ID for context menu items
*
*/
private enum GroupContextMenuItem implements IContextMenuItem {
Delete {
public String getText() {
return "Delete";
}
},
Edit {
public String getText() {
return "Edit";
}
};
}
/**
* Enum providing name and ID for context menu items
*
*/
private enum ChildContextMenuItem implements IContextMenuItem {
Delete {
public String getText() {
return "Delete";
}
},
MoveUp {
public String getText() {
return "Move Up";
}
},
MoveDown {
public String getText() {
return "Move Down";
}
};
}
/*
* (non-Javadoc)
*
* @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
boolean ownerImplementsEvents = true;
try {
fragmentOwner = (IBookshelfGUIEvents) activity;
} catch (ClassCastException e) {
ownerImplementsEvents = false;
}
if (!ownerImplementsEvents) {
throw new ClassCastException(activity.toString()
+ " does not implement "
+ IBookshelfGUIEvents.class.getName());
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Provide the layout of this fragment to the parent container
View view = inflater.inflate(R.layout.fragment_bookshelf, container,
false);
/**************************************************************/
/**//**/
/* Instantiate member variables */
/**//**/
/**************************************************************/
if (adapter == null) {
adapter = new ExpandableBookshelfAdapter(view.getContext(), null);
}
/**************************************************************/
/**//**/
/* Get button layout, and add a listener method to it */
/**//**/
/**************************************************************/
ImageButton addButton = (ImageButton) view.findViewById(R.id.addBook);
// adds a listener to the button
addButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Get the parent activity
// Make sure that the activity is not at the end of its
// lifecycle
if (getActivity() != null) {
// inform mainactivity this button has been pressed
addBookButtonPressed();
}
}
});
/******************************************************************************/
/**//**/
/* Get the list layout, and add listener methods and an adapter to it */
/**//**/
/******************************************************************************/
bookshelfList = (ExpandableListView) view
.findViewById(R.id.bookshelfList);
/*
* hides the by default visible arrow which indicates whether a group is
* expanded or not
*/
bookshelfList.setGroupIndicator(null);
// set the bookshelf list as a context menu
registerForContextMenu(bookshelfList);
// sets the bookshelf lists adapter
bookshelfList.setAdapter(adapter);
// Access the bookshelf reference
if (getArguments().getSerializable(Constants.Reference.BOOKSHELF) instanceof Bookshelf) {
bookshelfUpdated((Bookshelf) getArguments().getSerializable(
Constants.Reference.BOOKSHELF));
}
// return the view when it has been processed.
return view;
}
// these warnings can be suppressed as the type has already been checked
@SuppressWarnings("rawtypes")
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// check that the menuInfo is of the correct type
// this method is allowed to have quite a high cyclomatic complexity as
// it would otherwise cause code duplication
if (menuInfo instanceof ExpandableListContextMenuInfo
&& adapter != null) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
// get the provided book position
int bookIndex = ExpandableListView
.getPackedPositionGroup(info.packedPosition);
// trackIndex will be -1 if group is clicked
int trackIndex = ExpandableListView
.getPackedPositionChild(info.packedPosition);
// get the type of the context menu
int type = ExpandableListView
.getPackedPositionType(info.packedPosition);
// create an empty array to prevent trying to loop over an
// uninitialized variable
IContextMenuItem[] menuItems = new IContextMenuItem[0];
String title = "";
// fill the context menu with the correct items
if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
// get all menu items from the child context menu
menuItems = ChildContextMenuItem.values();
// set the context menu's title to that of the value of the
// child
title = adapter.getChild(bookIndex, trackIndex);
} else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
// get all menu items from the group context menu
menuItems = GroupContextMenuItem.values();
// set the context menu's title to that of the value of the book
title = adapter.getGroup(bookIndex);
}
// set the title
menu.setHeaderTitle(title);
// populate the context menu with items in the order they were
// declared in the enum declaration.
for (IContextMenuItem item : menuItems) {
// as this only loops when menuItems is of either of type
// GroupContextMenuItem[] or ChildContextMenuItem[], Enum can be
// used as a raw type
menu.add(Menu.NONE, ((Enum) item).ordinal(),
((Enum) item).ordinal(), item.getText());
}
}
}
/*
* (non-Javadoc)
*
* @see
* android.support.v4.app.Fragment#onContextItemSelected(android.view.MenuItem
* )
*/
@Override
public boolean onContextItemSelected(MenuItem item) {
if (getActivity() == null) {
return false;
}
// this method is allowed to have quite a high cyclomatic complexity as
// it would otherwise cause code duplication
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) item
.getMenuInfo();
// get the provided book position
int bookIndex = ExpandableListView
.getPackedPositionGroup(info.packedPosition);
// will be -1 if the type is group
int trackIndex = ExpandableListView
.getPackedPositionChild(info.packedPosition);
// get the type of the context menu
int type = ExpandableListView
.getPackedPositionType(info.packedPosition);
// create an empty array to prevent trying to loop over an uninitialized
// variable
IContextMenuItem[] menuItems = new IContextMenuItem[0];
// fill the array with the correct items
if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
menuItems = ChildContextMenuItem.values();
} else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
menuItems = GroupContextMenuItem.values();
}
// get an item and store it with its dynamic type
IContextMenuItem menuItem = menuItems[item.getItemId()];
// if the type of the context menu is group
if (menuItem instanceof GroupContextMenuItem) {
// perform the correct task
executeGroupMenuItem((GroupContextMenuItem) menuItem, bookIndex);
}
// if the type of the context menu is that of a child
else if (menuItem instanceof ChildContextMenuItem) {
// perform the correct task
executeChildMenuItem((ChildContextMenuItem) menuItem, bookIndex,
trackIndex);
}
return true;
}
/**
* Private method which executes the correct task depending on which menu
* item was chosen
*
* @param menuItem
* The menu item
* @param bookIndex
* The index of the book to execute the task on
*/
private void executeGroupMenuItem(GroupContextMenuItem menuItem,
int bookIndex) {
switch (menuItem) {
case Delete:
removeBook(bookIndex);
break;
case Edit:
// not fully implemented, some method to retrieve a real name should
// be used
setBookTitleAt(bookIndex, "NEWNAME");
break;
default:
break;
}
}
/**
* Private method which executes the correct task depending on which menu
* item was chosen
*
* @param menuItem
* The menu item
* @param bookIndex
* The index of the book containing the track
* @param trackIndex
* The index of the track to execute the task on
*/
private void executeChildMenuItem(ChildContextMenuItem menuItem,
int bookIndex, int trackIndex) {
switch (menuItem) {
case Delete:
removeTrack(bookIndex, trackIndex);
break;
case MoveUp:
moveTrack(bookIndex, trackIndex, -1);
break;
case MoveDown:
moveTrack(bookIndex, trackIndex, 1);
break;
default:
break;
}
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#moveTrack
* (int, int, int)
*/
public void moveTrack(int bookIndex, int trackIndex, int offset) {
fragmentOwner.moveTrack(bookIndex, trackIndex, offset);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeTrack
* (int, int)
*/
public void removeTrack(int bookIndex, int trackIndex) {
fragmentOwner.removeTrack(bookIndex, trackIndex);
}
/**
* @param bookIndex
* @param trackIndex
*/
private void childClicked(int bookIndex, int trackIndex) {
if (getActivity() != null) {
Log.d(TAG, "Child clicked at + [" + bookIndex + ", " + trackIndex
+ "]");
setSelectedTrack(bookIndex, trackIndex);
}
}
/**
* @param bookIndex
*/
private void groupClicked(int bookIndex) {
if (getActivity() != null) {
setSelectedBook(bookIndex);
}
}
/**
* Updates the adapter with the updated bookshelf and informs it that its
* data has been added.
*
* @param bs
* The updated bookshelf
*/
public void bookshelfUpdated(Bookshelf bs) {
Log.d(TAG, "Book added");
if (adapter != null) {
adapter.setBookshelf(bs);
adapter.notifyDataSetChanged();
}
}
/**
* @param newTime
*/
public void selectedBookElapsedTimeUpdated(int newTime) {
adapter.selectedBookElapsedTimeUpdated(newTime);
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfGUIEvents#
* addBookButtonPressed()
*/
public void addBookButtonPressed() {
fragmentOwner.addBookButtonPressed();
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#
* setSelectedBook(int)
*/
public void setSelectedBook(int bookIndex) {
fragmentOwner.setSelectedBook(bookIndex);
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#
* setSelectedTrack(int, int)
*/
public void setSelectedTrack(int bookIndex, int trackIndex) {
fragmentOwner.setSelectedTrack(bookIndex, trackIndex);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeBook
* (int)
*/
public void removeBook(int bookIndex) {
fragmentOwner.removeBook(bookIndex);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#removeTrack
* (int)
*/
public void removeTrack(int trackIndex) {
fragmentOwner.removeTrack(trackIndex);
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookshelfEvents#
* setBookTitleAt(int, java.lang.String)
*/
public void setBookTitleAt(int bookIndex, String newTitle) {
fragmentOwner.setBookTitleAt(bookIndex, "NEWNAME");
}
/**
* Private class used to populate the ExpandableListView used in
* BookshelfFragment.
*
* @author Fredrik �hs
*
*/
private class ExpandableBookshelfAdapter extends BaseExpandableListAdapter {
private static final int PROGRESSBAR_MAX = 100;
private static final int MILLIS_PER_SECOND = 1000;
private Context context;
private Bookshelf bookshelf;
// used to get synchronized time label and progress bar
private int bookElapsedTime;
private int bookProgress;
private View selectedBookView;
/**
* Constructs an ExpandableListAdapter with context and listData.
*
* @param context
* The context of the application.
* @param listData
* The data to be used to fill the list.
*/
public ExpandableBookshelfAdapter(Context context, Bookshelf bookshelf) {
this.context = context;
setBookshelf(bookshelf);
}
/**
* Updates the adapters class variables.
*
* @param newTime
* @return
*/
public void selectedBookElapsedTimeUpdated(int newTime) {
// if a second has passed
if (newTime / MILLIS_PER_SECOND > bookElapsedTime
/ MILLIS_PER_SECOND) {
// store this new value
bookElapsedTime = newTime;
// update the label
setTextViewText(
selectedBookView,
R.id.bookshelfBookPosition,
"Position: "
+ TextFormatter
.formatTimeFromMillis(bookElapsedTime));
// get duration of book in seconds
int bookDuration = bookshelf.getSelectedBookDuration();
// calculate the progress
int calculatedProgress = calculateProgress(newTime,
bookDuration);
// if there is no errors and the progress has progressed since
// the last saved progress
if ((calculatedProgress >= 0)
&& (calculatedProgress <= PROGRESSBAR_MAX)
&& (calculatedProgress > bookProgress)) {
// save this new progress
bookProgress = calculatedProgress;
// get the progressbar
ProgressBar pb = (ProgressBar) selectedBookView
.findViewById(R.id.bookshelfProgressBar);
// check that the progressbar is not null and set it to the
// new progress
if (pb != null) {
pb.setProgress(bookProgress);
}
}
}
}
/**
* Method that calculates a roofed progress, i.e. time = 51, duration =
* 1000 would return a progress of 6
*
* @param elapsedTime
* The time elapsed.
* @param duration
* The total duration.
* @return A roofed progress.
*/
private int calculateProgress(int elapsedTime, int duration) {
if (elapsedTime < 0 || duration <= 0) {
return 0;
}
int floored = PROGRESSBAR_MAX * elapsedTime / duration;
int rest = PROGRESSBAR_MAX * elapsedTime % duration;
if (rest == 0) {
return floored;
}
return floored + 1;
}
/**
* Set the bookshelf for when the data should update
*
* @param bookshelf
* The new bookshelf copy.
*/
public final void setBookshelf(Bookshelf bookshelf) {
this.bookshelf = bookshelf;
}
/**
* @param bookIndex
* The position of the book.
* @param trackIndex
* The position of the track.
* @return The title of the track at given position, null if incorrect
* index.
*/
public String getChild(int bookIndex, int trackIndex) {
if (bookshelf == null) {
return "";
}
return bookshelf.getTrackTitleAt(bookIndex, trackIndex);
}
/**
* @param bookIndex
* The position of the book.
* @param trackIndex
* The position of the track.
* @return The id of the track at given position.
*/
public long getChildId(int bookIndex, int trackIndex) {
return trackIndex;
}
/**
* @param bookIndex
* The position of the book.
* @return The amount of tracks in the book at given position.
*/
public int getChildrenCount(int bookIndex) {
if (bookshelf != null) {
return bookshelf.getNumberOfTracksAt(bookIndex);
}
return 0;
}
/**
* Converts the view of a track to display properly.
*
* @param bookIndex
* The position of the book.
* @param trackIndex
* The position of the track.
* @param isLastChild
* Whether the track is the last child in the expandable list
* view group.
* @param convertView
* The view to be converted.
* @param parent
* The view of the ExpandableListView.
* @return The converted view.
*/
public View getChildView(final int bookIndex, final int trackIndex,
boolean isLastChild, View convertView, ViewGroup parent) {
View cView = convertView;
if (cView == null) {
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// inflate it into parent.
cView = vi.inflate(R.layout.bookshelf_child_row, parent, false);
}
// the expandable list view is stored as final to use it in
// listeners
final ExpandableListView expandableListView = (ExpandableListView) parent;
// set the text of the child
setTextViewText(cView, R.id.bookshelfTrackTitle,
bookshelf.getTrackTitleAt(bookIndex, trackIndex));
// get the position of the track from the book
int duration = bookshelf.getTrackDurationAt(bookIndex, trackIndex);
// convert and set the duration of the track
setTextViewText(cView, R.id.bookshelfTrackTime,
TextFormatter.formatTimeFromMillis(duration));
cView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
trackClicked(bookIndex, trackIndex, expandableListView);
}
});
// set long click to show the child's context menu
cView.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
expandableListView.showContextMenu();
return false;
}
});
return cView;
}
/**
* Private method to be called when a track is clicked
*
* @param bookIndex
* The index of the book the track resides within
* @param trackIndex
* The index of the track
* @param expandableListView
* The expandable list view
*/
private void trackClicked(final int bookIndex, final int trackIndex,
final ExpandableListView expandableListView) {
// store the index of the book of the track clicked for
// correct redrawal
// force the expandable list view to redraw
expandableListView.invalidateViews();
// inform the bookshelfFragment's listener that a child has
// been selected
BookshelfFragment.this.childClicked(bookIndex, trackIndex);
// store for synchronization
selectedBookView = expandableListView.getChildAt(bookIndex);
try {
bookElapsedTime = bookshelf.getBookElapsedTime();
} catch (IndexOutOfBoundsException e) {
bookElapsedTime = 0;
}
bookProgress = calculateProgress(bookElapsedTime,
bookshelf.getSelectedBookDuration());
}
/**
* @param bookIndex
* The position of the book.
* @return The books title at given position
*/
public String getGroup(int bookIndex) {
if (bookshelf == null) {
return "";
}
try {
return bookshelf.getBookTitleAt(bookIndex);
} catch (IndexOutOfBoundsException e) {
return "";
}
}
/**
* @return The number of books.
*/
public int getGroupCount() {
if (bookshelf != null) {
return bookshelf.getNumberOfBooks();
}
return 0;
}
/**
* @param bookIndex
* The position of the book.
* @return The id of the book.
*/
public long getGroupId(int bookIndex) {
return bookIndex;
}
/**
* Converts the view of a book to display it properly.
*
* @param bookIndex
* The position of the book.
* @param isExpanded
* Whether the book is expanded or not.
* @param convertView
* The book's view.
* @param parent
* The expandable list view
* @return The converted view.
*/
public View getGroupView(final int bookIndex, final boolean isExpanded,
View convertView, ViewGroup parent) {
View cView = convertView;
if (cView == null) {
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// inflate it into its parent
cView = vi.inflate(R.layout.bookshelf_group_row, parent, false);
}
// set book as final to use in listeners
// set the expandableListView as final to use in listeners
final ExpandableListView expandableListView = (ExpandableListView) parent;
// get duration from book
int duration = bookshelf.getBookDurationAt(bookIndex);
// prevent problems with a duration of 0
if (duration == 0) {
return cView;
}
// get the elapsed time of the book
int time = 0;
int progress = 0;
// set title, author, time and duration of book
setTextViewText(cView, R.id.bookshelfBookTitle,
bookshelf.getBookTitleAt(bookIndex));
// set the color of the books title to red if selected,
if (bookIndex == bookshelf.getSelectedBookIndex()) {
setTextViewTextColor(cView, R.id.bookshelfBookTitle, Color.RED);
time = bookElapsedTime;
progress = bookProgress;
}
// and white otherwise.
else {
setTextViewTextColor(cView, R.id.bookshelfBookTitle,
Color.WHITE);
try {
time = bookshelf.getBookElapsedTime();
} catch (IndexOutOfBoundsException e) {
time = 0;
}
progress = calculateProgress(time, duration);
}
setTextViewText(cView, R.id.bookshelfAuthor,
bookshelf.getBookAuthorAt(bookIndex));
// format the string of the elapsed time of the book
String timeString = time == 0 ? "N/A" : TextFormatter
.formatTimeFromMillis(time);
setTextViewText(cView, R.id.bookshelfBookPosition, "Position: "
+ timeString);
setTextViewText(cView, R.id.bookshelfBookDuration, "Duration: "
+ TextFormatter.formatTimeFromMillis(duration));
// set the progress of the progress bar
ProgressBar progressBar = (ProgressBar) cView
.findViewById(R.id.bookshelfProgressBar);
// make sure progress is within its boundaries before setting it
if (progress >= 0 && progress <= PROGRESSBAR_MAX) {
progressBar.setProgress(progress);
}
// set cover art
ImageView imageView = (ImageView) cView
.findViewById(R.id.bookshelfBookCover);
// in future versions this cover art should be gotten from the file
imageView.setImageResource(R.drawable.img_no_cover);
// click the cover art to toggle the books tracks visibility
imageView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (isExpanded) {
expandableListView.collapseGroup(bookIndex);
} else {
expandableListView.expandGroup(bookIndex);
}
}
});
// sets the on click listener for the rest of the view
// store convertview as final to reach it inside this method
final View finalConvertView = cView;
cView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
bookClicked(bookIndex, isExpanded, expandableListView,
finalConvertView);
}
});
// set long click to show the group's context menu
cView.setOnLongClickListener(new OnLongClickListener() {
public boolean onLongClick(View v) {
expandableListView.showContextMenu();
return false;
}
});
return cView;
}
/**
* Private method to be called when a book is clicked.
*
* @param bookIndex
* Index of the book clicked
* @param isExpanded
* Whether or not the book is expanded in the expandable list
* view
* @param expandableListView
* The expandable list view
* @param convertView
* The view to be set as selected
*/
private void bookClicked(final int bookIndex, final boolean isExpanded,
final ExpandableListView expandableListView,
final View convertView) {
// expand the selected item
if (!isExpanded) {
expandableListView.expandGroup(bookIndex);
}
// scroll to the selected item
expandableListView.setSelectionFromTop(bookIndex, 0);
// invalidates views to force redraw thus setting the
// correct textcolor
expandableListView.invalidateViews();
// inform the BookshelfFragment that this button has been
// pressed.
BookshelfFragment.this.groupClicked(bookIndex);
// store the view so that text can update
selectedBookView = convertView;
try {
bookElapsedTime = bookshelf.getBookElapsedTime();
} catch (IndexOutOfBoundsException e) {
bookElapsedTime = 0;
}
bookProgress = calculateProgress(bookElapsedTime,
bookshelf.getSelectedBookDuration());
}
/**
* Private help class used to prevent code duplication
*
* @param view
* The TextView to where the text will be displayed.
* @param id
* The id of the TextView.
* @param text
* The text to be displayed.
*/
private void setTextViewText(View view, int id, String text) {
if (view != null) {
TextView textView = (TextView) view.findViewById(id);
if (textView != null) {
textView.setText(text);
}
}
}
/**
* Private help class used to prevent code duplication.
*
* @param view
* The TextView to where the text color will be set.
* @param id
* The id of the TextView.
* @param color
* The color to be set.
*/
private void setTextViewTextColor(View view, int id, int color) {
if (view != null) {
TextView textView = (TextView) view.findViewById(id);
if (textView != null) {
textView.setTextColor(color);
}
}
}
/*
* (non-Javadoc)
*
* @see android.widget.ExpandableListAdapter#hasStableIds()
*/
public boolean hasStableIds() {
return true;
}
/**
* @param bookIndex
* The position of the book.
* @param trackIndex
* The position of the track.
* @return Whether the track at given position is selectable.
*/
public boolean isChildSelectable(int bookIndex, int trackIndex) {
return true;
}
}
}